<?php

namespace pmvc\mvc\annotation;

use pmvc\annotation\AnnotationUtil;

use pmvc\mvc\controller\FormErrors;

use pmvc\util\ClassUtil;
use pmvc\util\ReflectionUtil;

use pmvc\mvc\Request;
use pmvc\mvc\Response;

use ReflectionClass;
use ReflectionMethod;
use ReflectionObject;

/**
 * A {@link Controller} that uses an annotated class.
 */
class AnnotatedController
	implements \pmvc\mvc\Controller {

	/**
	 * @var ReflectionMethod
	 */
	private $method;

	/**
	 * @var mixed
	 */
	private $controller;

	/**
	 * Creates the class.
	 * @param mixed $controller
	 * @param ReflectionMethod $method
	 */
	public function __construct($controller, ReflectionMethod $method) {
		$this->method		= $method;
		$this->controller	= $controller;
	}
		
	/**
	 * {@inheritDoc}
	 */
    public function handleRequest(Request $request, Response $response) {

    	// first look for a form binder
    	$methods = AnnotationUtil::findMethodsWithAnnotation(
    		new ReflectionObject($this->controller), 'pmvc\mvc\annotation\WebFormBinder');
    	$formBinderMethod = null;
    	if (!empty($methods)) {
    		$formBinderMethod = $methods[0];
    	}

    	// do form binding
    	$formBinder = null;
    	if (isset($formBinderMethod)) {
    		$args = $this->getInvocationArguments($formBinderMethod, $request, $response);
    		$formBinder = $formBinderMethod->invokeArgs($this->controller, $args);
    	}
 
    	// invoke controller method
    	$args = $this->getInvocationArguments($this->method, $request, $response, $formBinder);
    	return $this->method->invokeArgs($this->controller, $args);
    }

    /**
     * Returns invocation arguments in an array suitable
     * for calling {@link invokeArgs()} with on the given
     * method.
     * 
     * @param ReflectionMethod $method
     * @param Request $request
     * @param Response $response
     * @param WebFormBinder $formBinder
     * @return Array
     */
    private function getInvocationArguments(
    	ReflectionMethod $method, Request $request, Response $response,
    	\pmvc\mvc\WebFormBinder $formBinder=null) {

    	// get method parameters
    	$methodParams = $method->getParameters();

    	// build argument list
    	$args = Array();
    	foreach ($methodParams as $methodParam) {
    		$name 		= $methodParam->getName();
    		$class 		= $methodParam->getClass();
    		$isArray 	= $methodParam->isArray();
    		$className = (isset($class)) ? $class->getName() : null;
  
    		// Request
    		if (isset($class)
    			&& ReflectionUtil::classIsA($className, 'pmvc\mvc\Request')) {
    			$args []= $request;

    		// Response
    		} else if (isset($class)
    			&& ReflectionUtil::classIsA($className, 'pmvc\mvc\Response')) {
    			$args []= $response;

    		// Model
    		} else if (isset($class)
    			&& ReflectionUtil::classIsA($className, 'pmvc\mvc\Model')) {
    			$args []= $response->getModel();

    		// Dispatcher
    		} else if (isset($class)
    			&& ReflectionUtil::classIsA($className, 'pmvc\mvc\Dispatcher')) {
    			$args []= $request->getDispatcher();

    		// WebFormBinder
    		} else if (isset($class)
    			&& ReflectionUtil::classIsA($className, 'pmvc\mvc\WebFormBinder')) {
    			$args []= $formBinder;

    		// FormErrors
    		} else if (isset($class)
    			&& ReflectionUtil::classIsA($className, 'pmvc\mvc\controller\FormErrors')) {
    			$args []= new FormErrors();
    			
    		// path variables
    		} else if (!isset($class) && $request->hasPathVariable($name)) {
    			$args []= $request->getPathVariable($name.($isArray?"[]":""));
    			
    		// request parameters
    		} else if (!isset($class) && $request->hasParameter($name)) {
    			$args []= $request->getParameter($name.($isArray?"[]":""));

    		// default value
    		} else if ($methodParam->isDefaultValueAvailable()) {
    			$args []= $methodParam->getDefaultValue();

    		// unknown parameter
    		} else {
    			$args []= null;
    			
    		}
    	}
 
    	// return it
    	return $args;
    }
}

?>